----------------------------------------------------
-- Meme Faces by Woovie                           --
-- All script below was written fresh. None of it --
-- is copied code, nor any resource included with --
-- this script. If you have any concerns, conact  --
-- j0rdanimal [at] gmail.com                      --
----------------------------------------------------

local imageTitles = {}
local imageNames = {}
local tempData = {}
local x, y = guiGetScreenSize()
tempData.showing = false
tempData.handled = false

function resourceStart()
	---------------
	-- XML Load ---
	---------------
	local xml = xmlLoadFile ( "faces.xml" )
	local xmls = xmlNodeGetChildren ( xml )
	for k,v in ipairs ( xmls ) do
		imageTitles[k] = xmlNodeGetAttribute ( v, "name" )
		imageNames[k] = xmlNodeGetAttribute ( v, "file" )
	end
	---------------
	-- Base Data --
	---------------
	tempData.render = "true"
	tempData.color = tocolor ( ( math.random ( 100, 255 ) ), ( math.random ( 100, 255 ) ), ( math.random ( 100, 255 ) ), 255 )
	tempData.face = math.random ( 1, #imageNames )
	if not getElementData ( getLocalPlayer(), "memeLogged" ) then
		setElementData ( getLocalPlayer(), "memeRender", tempData.render )
		setElementData ( getLocalPlayer(), "memeFace", tempData.face )
		setElementData ( getLocalPlayer(), "memeColor", tempData.color )
		memeShouldRender()
	else
		tempData.render = getElementData ( getLocalPlayer(), "memeRender" )
		tempData.face = getElementData ( getLocalPlayer(), "memeFace" )
		tempData.color = getElementData ( getLocalPlayer(), "memeColor" )
		memeShouldRender()
	end
	---------------
	-- GUI --------
	---------------
	meme_wnd = guiCreateWindow ( x / 2 - 200, y / 2 - 200, 400, 400, "Meme Faces by Woovie", false )
	meme_wnd_btn1 = guiCreateButton ( 220, 40, 180, 30, "Apply", false, meme_wnd )
	meme_wnd_btn2 = guiCreateButton ( 220, 120, 180, 30, "Color", false, meme_wnd )
	meme_wnd_btn3 = guiCreateButton ( 220, 160, 180, 30, "Close", false, meme_wnd )
	meme_wnd_btn4 = guiCreateButton ( 220, 80, 180, 30, "Reset Selection", false, meme_wnd )
	local loc = math.random ( 1, #imageNames )
	meme_wnd_chk1 = guiCreateCheckBox ( 250, 190, 110, 30, "Render Faces ?", true, false, meme_wnd )
	meme_wnd_lst1 = guiCreateGridList ( 0, 20, 200, 480, false, meme_wnd )
	local column = guiGridListAddColumn ( meme_wnd_lst1, "Meme Name", 1 )
	setupGridList()
	addEventHandler ( "onClientGUIClick", meme_wnd_btn1, memeApplyData, false )
	addEventHandler ( "onClientGUIClick", meme_wnd_btn2, function() colorPicker.openSelect() end, false )
	addEventHandler ( "onClientGUIClick", meme_wnd_btn3, function() memeWindow ( false ) end, false )
	addEventHandler ( "onClientGUIClick", meme_wnd_btn4, resetWindow, false )
	addEventHandler ( "onClientGUIClick", meme_wnd_lst1, reloadGUIFace, false )
	addEventHandler ( "onClientGUIDoubleClick", meme_wnd_lst1, memeApplyData, false )
	guiSetVisible ( meme_wnd, false )
	bindKey ( "F3", "up", memeWindow )
end

function setupGridList()
	local column = 1
	guiGridListClear ( meme_wnd_lst1 )
	for k,v in ipairs ( imageNames ) do
		local row = guiGridListAddRow ( meme_wnd_lst1 )
		guiGridListSetItemText ( meme_wnd_lst1, row, column, imageTitles[k], false, false )
		guiGridListSetItemData ( meme_wnd_lst1, row, column, imageNames[k] )
	end
end

function resetWindow()
	reloadGUIFace()
	playerLogin()
end
function playerLogin()
	tempData.render = getElementData ( getLocalPlayer(), "memeRender" )
	tempData.face = getElementData ( getLocalPlayer(), "memeFace" )
	tempData.color = getElementData ( getLocalPlayer(), "memeColor" )
	setupGridList()
end

function closedColorPicker ( r, g, b, a )
	local color = tocolor ( r, g, b, a )
	tempData.color = color
end

function memeApplyData()
	tempData.render = tostring ( guiCheckBoxGetSelected ( meme_wnd_chk1 ) )
	setElementData ( getLocalPlayer(), "memeRender", tempData.render )
	setElementData ( getLocalPlayer(), "memeFace", tempData.face )
	setElementData ( getLocalPlayer(), "memeColor", tempData.color )
	triggerServerEvent ( "memeSave", getLocalPlayer(), tempData.render, tempData.face, tempData.color )
end

function reloadGUIFace()
	local row, column = guiGridListGetSelectedItem ( meme_wnd_lst1 )
	local face = guiGridListGetItemData ( meme_wnd_lst1, row, column )
	for k,v in ipairs ( imageNames ) do
		if v == face then
			tempData.face = k
		end
	end
end

function memeWindow ()
	if tempData.showing == false then
		tempData.render = getElementData ( getLocalPlayer(), "memeRender" )
		tempData.face = getElementData ( getLocalPlayer(), "memeFace" )
		tempData.color = getElementData ( getLocalPlayer(), "memeColor" )
		guiCheckBoxSetSelected ( meme_wnd_chk1, toboolean ( tempData.render ) )
		guiGridListSetSelectedItem ( meme_wnd_lst1, ( tempData.face - 1 ), 1 )
		addEventHandler ( "onClientRender", root, memeGUI )
		showCursor ( true )
		guiSetVisible ( meme_wnd, true )
		tempData.showing = true
	elseif tempData.showing == true then
		removeEventHandler ( "onClientRender", root, memeGUI )
		showCursor ( false )
		guiSetVisible ( meme_wnd, false )
		tempData.showing = false
	end
end

function memeGUI()
	local face = imageNames [tempData.face]
	local wx, wy = guiGetPosition ( meme_wnd, false )
	dxDrawImage ( ( wx + 230 ), ( wy + 230 ), 150, 150, "images/"..face, 0, 0, 0, tempData.color, true )
end

function memeShouldRender()
	if tempData.render == "true" and tempData.handled == false then
		addEventHandler ( "onClientRender", root, renderFaces )
		tempData.handled = true
	elseif tempData.render == "false" and tempData.handled == true then
		removeEventHandler ( "onClientRender", root, renderFaces )
		tempData.handled = false
	end
end

function toboolean ( what )
	if what == "true" or what == 1 then
		return true
	elseif what == "false" or what == 0 then
		return false
	end
end

function renderFaces()
	for k,v in ipairs ( getElementsByType ( "player" ) ) do
		renderHead ( v )
	end
end

-- Most math here done originally by Devan_LT, but I've rewritten it so I can understand it and others can too
function renderHead ( player )
	local num = getElementData ( player, "memeFace" )
	local face = imageNames [num] -- Get the actual image name
	if not face then return end
	if not isElementStreamedIn ( player ) then return end -- If the player isn't streamed in on client, cancel render.
	local head_x, head_y, head_z = getPedBonePosition ( player, 8 ) -- Get the center head bone
	local head_tx, head_ty, head_tz = getPedBonePosition ( player, 5 ) -- Bone for the top of the head
	local head_bx, head_by, head_bz = getPedBonePosition ( player, 6 ) -- Bone for the bottom of the head
	head_z = head_z + 0.06 -- Gets face centered a bit better
	local scr_x, scr_y = getScreenFromWorldPosition ( head_x, head_y, head_z, 0, false ) -- Get location of head center bone on screen
	local scr_tx, scr_ty = getScreenFromWorldPosition ( head_tx, head_ty, head_tz, 0, false ) -- Get location of head top bone on screen
	local scr_bx, scr_by = getScreenFromWorldPosition ( head_bx, head_by, head_bz, 0, false ) -- Get location of head bottom bone on screen
	if scr_x and scr_tx then -- If center bone and top bone are on screen
		local cam_x, cam_y, cam_z, lok_x, lok_y, lok_z, cam_rol, cam_fov = getCameraMatrix() -- Camera matrix
		local dist = getDistanceBetweenPoints3D ( head_x, head_y, head_z, cam_x, cam_y, cam_z ) -- Distance between camera and head center bone
		if  dist < 0.4 or dist > 90 or not scr_bx then return end -- If the cam is too close or too far from head center bone, or if the head bottom bone isn't visible, cancel render.
		if not isLineOfSightClear ( cam_x, cam_y, cam_z, head_x, head_y, head_z, true, false, true, true, false, true, false, player ) then return end -- Line of sight check. Read the wiki to understand this.
		dxDrawImage ( scr_x - ( 80 / dist ) * x / 800 / 70 * cam_fov, scr_y - ( 100 / dist ) * y / 600 / 70 * cam_fov, ( 160 / dist ) * x / 800 / 70 * cam_fov, ( 200 / dist ) * y / 600 / 70 * cam_fov, "images/"..face, -math.deg ( math.atan2 ( scr_tx - scr_bx, scr_ty - scr_by ) ), 0, 0, getElementData ( player, "memeColor" ), false )
	end
end

addEvent ( "memeSaved", true )
addEvent ( "memeRenderUpdate", true )
addEvent ( "onClientPlayerLogin", true )
addEvent ( "onClientPlayerLogout", true )
addEventHandler ( "memeSaved", getRootElement(), memeShouldRender )
addEventHandler ( "memeRenderUpdate", root, memeShouldRender )
addEventHandler ( "onClientPlayerLogin", root, playerLogin )
addEventHandler ( "onClientPlayerLogout", root, setupGridList )
addEventHandler ( "onClientResourceStart", resourceRoot, resourceStart )
addCommandHandler ( "meme", memeWindow )